﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Diagnostics;

namespace PasswordProvider.ViewModel.Framework
{
    /// <summary>
    /// Base class for all ViewModel classes in the application.
    /// It provides support for property change notifications 
    /// and clean up of resources such as event handlers. This class is abstract.
    /// </summary>
    public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
    {
        #region Events

        /// <summary>        
        /// Event args for a changed event. 
        /// </summary>
        internal class ValueChangedEventArgs<T> : EventArgs
        {
            public ValueChangedEventArgs(T oldValue, T newValue)
            {
                OldValue = oldValue;
                NewValue = newValue;
            }
            public bool Cancel { get; set; }
            public T OldValue { get; set; }
            public T NewValue { get; set; }

        }

        // TODO: It would probably be better to remove ValueChangedEventHandler entirely and replace all events that use it with 
        // EventHandler using ValueChangedEventArgs above.        
        
        /// <summary>        
        /// A commonly used event handler, declared here so all inheritors can use it.
        /// </summary>
        internal delegate void ValueChangedEventHandler<T>(T param);

        #endregion

        #region Constructor

        internal protected ViewModelBase()
        {
        }
        internal protected ViewModelBase(string displayName) 
        {
            _displayName = displayName;        
        }

        #endregion // Constructor
        
        #region Public Properties

        /// <summary>
        /// Get or set the display name.
        /// </summary>
        public virtual string DisplayName
        {
            get 
            {
                return _displayName; 
            }
            set 
            {
                this.SetPropertyValue(value, ref _displayName, "DisplayName"); 
            }
        } private string _displayName;

        /// <summary>
        /// Returns true if a value has been changed (INotifyPropertyChanged fired at least once.)
        /// </summary>
        public virtual bool IsChanged
        {
            get;
            private set;
        }

        #endregion // Constructor

        #region Debugging Aides

        /// <summary>
        /// Warns the developer if this object does not have
        /// a public property with the specified name. This 
        /// method does not exist in a Release build.
        /// </summary>
        [Conditional("DEBUG")]
        [DebuggerStepThrough]
        public void VerifyPropertyName(string propertyName)
        {
            // Verify that the property name matches a real,  
            // public, instance property on this object.
            if (TypeDescriptor.GetProperties(this)[propertyName] == null)
            {
                Debug.Fail("Invalid property name: " + propertyName);
            }
        }

        #endregion // Debugging Aides

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Raised when a property on this object has a new value.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises this object's PropertyChanged event.
        /// </summary>
        /// <param name="propertyName">The property that has a new value.</param>
       internal protected virtual void OnPropertyChanged(string propertyName)
        {
            this.VerifyPropertyName(propertyName);

            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
            IsChanged = true;
        }

        /// <summary>
        /// Generic method to set a property value with equality 
        /// checking and raising the property changed event.
        /// </summary>
        internal protected bool SetPropertyValue<T>(T value, ref T field, string propertyName)
        {
            if ((value != null && !value.Equals(field)) || (value == null && field != null))
            {
                field = value;
                OnPropertyChanged(propertyName);
                return true;
            }
            return false;
        }


        
        #endregion // INotifyPropertyChanged Members

        #region IDisposable Members

        /// <summary>
        /// Invoked when this object is being removed from the application
        /// and will be subject to garbage collection.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);

        }

        /// <summary>
        /// Child classes can override this method to perform 
        /// clean-up logic, such as removing event handlers.
        /// </summary>
        protected virtual void Dispose(bool disposing)
        {
        }

#if DEBUG
        /// <summary>
        /// Useful for ensuring that ViewModel objects are properly garbage collected.
        /// </summary>
        ~ViewModelBase()
        {
            string msg = null;

            if (this is IRequestClose)
                msg = "#### - :";
            if (this is SitesListViewModel)
                msg = "**** -- :";
            if (this is ICommitChanges)
                msg = msg + "@#&!@)( -- :";
            if (this is MessageBoxViewModel)
                msg = msg + "$$$$  -- :";

            msg += string.Format("{0} ({1}) ({2}) Finalized", this.GetType().Name, this.DisplayName, this.GetHashCode());
            System.Diagnostics.Debug.WriteLine(msg);
        }
#endif

        #endregion // IDisposable Members
    }
}
